using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.IO;
using System.Xml;
using System.Net;
using System.Net.Sockets;
using System.Net.Security;
using System.Text;
using Rest;
using Rest.Transport;

namespace Util
{
    /// <summary>
    /// This is designed to behave like a System.Net.WebRequest but gives more control.
    /// for example, System.Net.WebRequest throws exceptions based on response headers.
    /// There is a workaround for this though: accesing the Response or Status members of
    /// WebException.
    /// </summary>
    public class RestWebRequest
    {
        /// <summary>
        /// ctor
        /// </summary>
        public RestWebRequest()
        {
            //
            // TODO: Add constructor logic here
            //
        }

        /// <summary>
        /// Gets the text returned from the resource
        /// </summary>
        /// <param name="uri"></param>
        /// <param name="requestText"></param>
        /// <returns></returns>
        /// <exception cref="SocketException" />
        public String GetResponseText(Uri uri, String requestText)
        {
            StringBuilder headersBuilder = new StringBuilder();

            using (TcpClient tcpClient = OpenConnection(uri, requestText))
            {
                using (StreamReader reader = new StreamReader(tcpClient.GetStream()))
                {
                    while (reader.Peek() > 0)
                    {
                        headersBuilder.AppendLine(reader.ReadLine());
                    }
                }
            }

            return headersBuilder.ToString();
        }

        /// <summary>
        /// Returns a basic authorization header
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        private String CreateBasicAuthorizationHeader(String username, String password)
        {
            Byte[] credentialBytes = Encoding.ASCII.GetBytes(
                username + ":" + password);

            return String.Format("Authorization: Basic {0}\r\n",
                Convert.ToBase64String(credentialBytes));
        }

        /// <summary>
        /// Creates HTTP 1.1 request header text
        /// </summary>
        /// <param name="verb">One of the standard HTTP verbs, HEAD, GET etc</param>
        /// <param name="uri">The URI to connect to</param>
        /// <param name="credential">Network credential for authentication</param>
        /// <param name="additionalHeaders">Any additional headers to be passed to the 
        /// resource</param>
        /// <returns></returns>
        public String CreateRequest(
            String verb,
            Uri uri,
            NetworkCredential credential,
            WebHeaderCollection additionalHeaders)
        {
            StringBuilder headerBuilder = new StringBuilder();

            headerBuilder.Append(
                verb + " " + uri.PathAndQuery + " HTTP/1.1\r\n" +
                "Host: " + uri.Host + "\r\n");

            if (credential != null)
            {
                headerBuilder.AppendFormat(
                    CreateBasicAuthorizationHeader(credential.UserName,
                    credential.Password)
                    );
            }

            if (additionalHeaders != null)
            {
                foreach (String key in additionalHeaders)
                {
                    headerBuilder.AppendFormat("{0}: {1}\r\n", key,
                        additionalHeaders[key]);
                }
            }

            // The all important close header
            headerBuilder.Append("Connection: Close\r\n");
            headerBuilder.Append("\r\n");

            return headerBuilder.ToString();
        }

        /// <summary>
        /// Opens a TcpClient on the supplied URI. 
        /// Use it to read from the stream.
        /// </summary>
        /// <param name="httpVerb"></param>
        /// <param name="uri"></param>
        /// <returns></returns>
        /// <exception cref="SocketException">
        /// The resource may refuse connections. 
        /// Perhaps does not listen on the supplied port.
        /// </exception>
        public TcpClient OpenConnection(
            String httpVerb,
            Uri uri,
            NetworkCredential credential,
            WebHeaderCollection additionalHeaders)
        {
            StringBuilder getRequestBuilder = new StringBuilder();

            String requestedFile = uri.PathAndQuery;

            getRequestBuilder.Append(CreateRequest(httpVerb, uri, credential,
                additionalHeaders));

            return OpenConnection(uri, getRequestBuilder.ToString());
        }

        /// <summary>
        /// Opens a TcpClient on the supplied URI by passing the request to it
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        private TcpClient OpenConnection(Uri uri, String requestText)
        {
            Byte[] request = null;

            TcpClient tcpClient = new TcpClient();
            tcpClient.Connect(uri.DnsSafeHost, uri.Port);
            tcpClient.ReceiveBufferSize = 256;
            tcpClient.ReceiveTimeout = 10;

            NetworkStream stream = tcpClient.GetStream();

            request = Encoding.ASCII.GetBytes(requestText);
            stream.Write(request, 0, request.Length);
            stream.Flush();

            return tcpClient;
        }
    }
}